use core::fmt::Write;
use core::ptr;

use super::{DMA1, PA, RCC, USART1};
use cmsis_rtos2::event_flags::EventFlags;
use cmsis_rtos2::message_queue::{msg_queue_mem_size, MessageQueue};
use cmsis_rtos2::{osWaitForever, Store};
use log::{set_logger, set_max_level, LevelFilter, Log};
use stm32f1::stm32f103::interrupt;
use stm32f1::stm32f103::{Interrupt, NVIC};

static RECV_BUF: Store<256> = Store::const_new();
static mut RECV_INDEX: usize = 0;

const MQ_SIZE: usize = msg_queue_mem_size(10, size_of::<&'static[u8]>());
static mut RECV_MQ: MessageQueue<&'static[u8], 10, MQ_SIZE> = MessageQueue::const_new();

static WRITE_BUF: Store<256> = Store::const_new();
static mut EVENT_FLAGS: EventFlags = EventFlags::const_new();

const FLAG_DMA_TC: u32 = 0x01;
const FLAG_LOG: u32 = 0x02;

// PA9 <=> TX <=> DMA1_Channel4
// PA10 <=> RX <=> DMA1_Channel5

pub fn init() {
    RCC.apb2enr
        .modify(|_, w| w.iopaen().enabled().usart1en().enabled());
    RCC.ahbenr.modify(|_, w| w.dma1en().enabled());

    PA.crh.modify(|_, w| {
        w.mode9()
            .output50()
            .cnf9()
            .alt_push_pull()
            .mode10()
            .input()
            .cnf10()
            .open_drain()
    });
    USART1.cr1.reset();
    USART1.cr2.reset();
    USART1
        .cr3
        .modify(|_, w| w.dmar().enabled().dmat().enabled());
    USART1.brr.write(|w| unsafe { w.bits(72000000 / 115200) });
    USART1.cr1.write(|w| {
        w.ue()
            .enabled()
            .idleie()
            .enabled()
            .te()
            .enabled()
            .re()
            .enabled()
    });

    DMA1.ifcr.write(|w| w.ctcif4().clear());
    DMA1.ch5.cr.reset();
    DMA1.ch5
        .mar
        .write(|w| unsafe { w.bits(RECV_BUF.address()) });
    DMA1.ch5
        .par
        .write(|w| unsafe { w.bits(USART1.dr.as_ptr() as u32) });
    DMA1.ch5.ndtr.write(|w| w.ndt().bits(RECV_BUF.len() as u16));
    DMA1.ch5.cr.write(|w| {
        w.minc()
            .enabled()
            .pinc()
            .disabled()
            .dir()
            .from_peripheral()
            .circ()
            .enabled()
            .msize()
            .bits8()
            .psize()
            .bits8()
            .en()
            .enabled()
    });

    unsafe {
        EVENT_FLAGS.create("uart1");
        EVENT_FLAGS.set_flags(FLAG_DMA_TC | FLAG_LOG);

        RECV_MQ.create("uart1");

        // enable
        NVIC::unmask(Interrupt::USART1);
        NVIC::unmask(Interrupt::DMA1_CHANNEL4);
    }

    _ = set_logger(&LogUart);
    set_max_level(LevelFilter::Trace);
    
}

pub fn uart_write(buf: &[u8]) {
    unsafe {
        EVENT_FLAGS.wait_any_flags(FLAG_LOG, osWaitForever);
        write_by_dma(buf);
        EVENT_FLAGS.set_flags(FLAG_LOG);
    }
}

fn write_by_dma(buf: &[u8]) {
    unsafe {
        EVENT_FLAGS.wait_any_flags(FLAG_DMA_TC, osWaitForever);
        write_impl(buf);
    }
}

pub struct LogUart;

impl Write for LogUart {
    fn write_str(&mut self, s: &str) -> core::fmt::Result {
        write_by_dma(s.as_bytes());
        Ok(())
    }
}

impl Log for LogUart {
    fn enabled(&self, _metadata: &log::Metadata) -> bool {
        true
    }

    fn flush(&self) {}

    fn log(&self, record: &log::Record) {
        unsafe {
            EVENT_FLAGS.wait_any_flags(FLAG_LOG, osWaitForever);
        }
        _ = writeln!(
            LogUart,
            "[{}] {}:{} {}",
            record.level(),
            record.file_static().unwrap_or(""),
            record.line().unwrap_or(0),
            record.args()
        );
        unsafe {
            EVENT_FLAGS.set_flags(FLAG_LOG);
        }
    }
}

pub fn uart_recv() -> &'static[u8] {
    unsafe {
        RECV_MQ.poll()
    }
}

unsafe fn write_impl(buf: &[u8]) {
    ptr::copy_nonoverlapping(buf.as_ptr(), WRITE_BUF.c_ptr() as *mut u8, buf.len());

    DMA1.ch4.cr.reset();
    DMA1.ch4.par.write(|w| w.bits(USART1.dr.as_ptr() as u32));
    DMA1.ch4.mar.write(|w| w.bits(WRITE_BUF.address()));
    DMA1.ch4.ndtr.write(|w| w.bits(buf.len() as u32));
    DMA1.ch4.cr.write(|w| {
        w.dir()
            .from_memory()
            .minc()
            .enabled()
            .tcie()
            .enabled()
            .en()
            .enabled()
    });
}

unsafe fn recv_idle() {
    let index = RECV_BUF.len() - DMA1.ch5.ndtr.read().ndt().bits() as usize;
    let recv_index = RECV_INDEX;
    if index == recv_index {
        return;
    }
    if index > recv_index {
        let data = RECV_BUF.slice(recv_index, index - recv_index);
        RECV_MQ.put(data);
    } else {
        let data = RECV_BUF.slice(recv_index, RECV_BUF.len() - recv_index);
        RECV_MQ.put(data);
        if index != 0 {
            let data = RECV_BUF.slice(0, index);
            RECV_MQ.put(data);
        }
    }
    RECV_INDEX = index;
}

#[interrupt]
unsafe fn USART1() {
    if USART1.sr.read().idle().bit_is_set() {
        USART1.dr.read();
        recv_idle();
    }
}

#[interrupt]
fn DMA1_CHANNEL4() {
    if DMA1.isr.read().tcif4().is_complete() {
        DMA1.ifcr.write(|w| w.ctcif4().clear());
        unsafe {
            EVENT_FLAGS.set_flags(FLAG_DMA_TC);
        }
    }
}
